﻿using System;
using Const;
using Const.util;
using UnityEngine;

public class PlayController : MonoBehaviour
{
    public Line line;

    private Rigidbody _rigidbody;
    private Transform _transform;

    private bool _isLink;
    private bool _isDown;


    /**
     * 加速度
     */
    private float _omga;


    private Vector3 _linkPos;
    private Vector3 _addVec3;
    private Vector3 _clickPos;
    private Vector3 _clickFwd;


    public void ChangeState(EState value)
    {
        switch (value)
        {
            case EState.START:
                _transform.position = new Vector3(6f, -3f, 0);
                _rigidbody.useGravity = true;
                break;
            case EState.ING:
                SetVelocity(_transform.position, _linkPos, -Param.playerSpeed / 5);
                break;
            case EState.END:
                _rigidbody.useGravity = false;
                _rigidbody.velocity = Vector3.zero;
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(value), value, null);
        }
    }

    void Start()
    {
        _rigidbody = gameObject.GetComponent<Rigidbody>();
        _transform = gameObject.GetComponent<Transform>();
    }

    void FixedUpdate()
    {
        if (!_isLink) return;
        var fp = _linkPos - _transform.position;
        fp = fp.normalized * (_rigidbody.mass * _omga);
        fp += -Physics.gravity; //取消掉重力
        _rigidbody.AddForce(fp, ForceMode.Force);
    }

    // ReSharper disable Unity.PerformanceAnalysis
    void Update()
    {
        switch (GameManager.ins.state)
        {
            case EState.START:
            {
                if (!_isLink)
                {
                    var tempPos = GetLinkPos(_transform.position, Vector3.up);
                    if (!tempPos.Equals(Vector3.zero))
                    {
                        _linkPos = tempPos;
                        line.DrawLine(_transform.position, _linkPos);
                        SetVelocity(_transform.position, _linkPos, -Param.playerSpeed / 5);
                        _isLink = true;
                    }
                }
                else
                {
                    var angle = Utils.Angle(_transform.position, _linkPos);
                    if (angle > -70)
                    {
                        SetVelocity(_transform.position, _linkPos, Param.playerSpeed / 5);
                    }
                    else if (angle < -110)
                    {
                        SetVelocity(_transform.position, _linkPos, -Param.playerSpeed / 5);
                    }

                    line.DrawLine(_transform.position, _linkPos);
                }

                break;
            }
            case EState.ING:
            {
                if (Input.GetMouseButtonDown(0))
                {
                    _isDown = true;
                    _clickPos = _transform.position;
                    _clickFwd = _transform.TransformDirection(Param.forward);
                    _addVec3 = _transform.position;
                }
                else if (Input.GetMouseButtonUp(0))
                {
                    _isDown = false;
                }

                if (_isDown)
                {
                    if (!_isLink)
                    {
                        if (_linkPos.Equals(Vector3.zero))
                        {
                            var tempPos = GetLinkPos(_clickPos, _clickFwd);
                            if (!tempPos.Equals(Vector3.zero))
                            {
                                _linkPos = tempPos;
                            }
                        }
                        else
                        {
                            var tempPos = Vector3.Lerp(_addVec3, _linkPos, Param.drawLineSpeed);
                            var len = (int) ((tempPos - _linkPos).sqrMagnitude);
                            if (len <= 1)
                            {
                                SetVelocity(_transform.position, _linkPos, -Param.playerSpeed);
                                line.DrawLine(_transform.position, _linkPos);
                            }
                            else
                            {
                                line.DrawLine(_transform.position, tempPos);
                                _addVec3 = tempPos;
                            }
                        }
                    }
                    else
                    {
                        line.DrawLine(_transform.position, _linkPos);
                    }
                }
                else
                {
                    line.ClearLine();
                    _isLink = false;
                    _linkPos = Vector3.zero;
                    _addVec3 = Vector3.zero;
                }

                break;
            }
            case EState.END:
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }


    /**
     * 获取2维向量的切线（法向量）这里直接凑方便用Vector3了
     */
    private Vector3 GetTangent(Vector3 a)
    {
        var x = (float) (a.x * Math.Cos(Math.PI / 2) - a.y * Math.Sin(Math.PI / 2));
        var y = (float) (a.x * Math.Sin(Math.PI / 2) + a.y * Math.Cos(Math.PI / 2));
        return new Vector3(x, y, 0);
    }

    private void OnTriggerEnter(Collider other)
    {
        //TODO 隐藏player ,播放粒子效果 , 切换游戏状态 ,显示游戏结束
        Debug.Log("GameOver");
        line.ClearLine();
        GameManager.ins.state = EState.END;
    }

    // ReSharper disable Unity.PerformanceAnalysis
    //TODO 是不是下一帧就能找到碰撞点
    private Vector3 GetLinkPos(Vector3 origin, Vector3 forward)
    {
        if (Physics.Raycast(origin, forward, out var hitInfo, 100f, LayerMask.GetMask("wall")))
        {
            var cube = hitInfo.transform.gameObject.GetComponentInParent<Cube>();
            if (cube != null)
            {
                return cube.LinkPos();
            }
        }

        return Vector3.zero;
    }

    private void SetVelocity(Vector3 from, Vector3 to, float defaultSpeed = Param.playerSpeed)
    {
        var fp = to - from;
        var fp3 = GetTangent(fp).normalized * defaultSpeed;
        var speed = fp3.magnitude;
        var radius = fp.magnitude;
        _omga = speed * speed / radius;
        _rigidbody.velocity = fp3;
    }
}